home *** CD-ROM | disk | FTP | other *** search
- /*
- File: Fragments.c
-
- Contains: Code Fragment manipulation routines
-
- Written by: Chris White, Developer Technical Support
-
- Copyright: © 1995 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- 9/28/95 CW First release
-
- */
-
- #ifndef __MEMORY__
- #include <Memory.h>
- #endif
-
- #ifndef __RESOURCES__
- #include <Resources.h>
- #endif
-
- #ifndef __STDDEF__
- #include <stddef.h>
- #endif
-
- #ifndef __STRING__
- #include <string.h>
- #endif
-
-
-
- #ifndef __FRAGMENTTOOL__
- #include "FragmentTool.h"
- #endif
-
- #ifndef __FRAGMENTSTUFF__
- #include "FragmentStuff.h"
- #endif
-
- #include "Prototypes.h"
-
-
- #include "Utilities.h"
-
-
-
- static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount );
- static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength );
-
-
-
-
-
-
- //
- // Translates the 'cfrg' resource into our own data structure. Ours will
- // contain the same information in a simpler format, plus a few extra fields.
- //
- OSErr ParseResource ( Handle theResource, tHeaderHan privateData )
- {
- // Copy the relevant items from the 'cfrg' resource into our internal format
- SignedByte oldResourceState, oldInternalState;
- short itemCount, index;
- Ptr itemStart;
- long headerSize = offsetof(cfrgHeader, arrayStart);
- OSErr err = noErr;
-
-
- // set the size of the destination block
- itemCount = (*(hdrHand)theResource)->itemCount;
- err = SetInternalResourceSize ( (Handle) privateData, itemCount );
- if ( err )
- goto done;
-
- // Lock the original resource
- oldResourceState = HGetState ( theResource );
- HLock ( theResource );
-
- oldInternalState = HGetState ( (Handle) privateData );
- HLock ( (Handle) privateData );
-
- // Copy the relevant fields from the header
- (*privateData)->version = (*(hdrHand)theResource)->version;
- (*privateData)->itemCount = itemCount;
-
- // Transfer each item from the cfrg into the internal resource
- if (itemCount == 0) goto done;
- itemStart = &(*(hdrHand)theResource)->arrayStart;
- for (index = 0; index < itemCount; index++) {
- cfrgItem* srcItem;
- tItemPtr dstItem;
-
- srcItem = (cfrgItem*)itemStart;
- dstItem = &(*privateData)->itemList[index];
-
- // These are just used internally
- dstItem->bDeleted = false;
- dstItem->bExistsInDocument = true;
- dstItem->tempFilePtr = nil;
-
- // These contain the actual fargement data
- dstItem->archType = srcItem->archType;
- dstItem->updateLevel = srcItem->updateLevel;
- dstItem->currVersion = srcItem->currVersion;
- dstItem->oldDefVersion = srcItem->oldDefVersion;
- dstItem->appStackSize = srcItem->appStackSize;
- dstItem->appSubFolder = srcItem->appSubFolder;
- dstItem->usage = srcItem->usage;
- dstItem->location = srcItem->location;
- dstItem->codeOffset = srcItem->codeOffset;
- dstItem->codeLength = srcItem->codeLength;
- BlockMove(srcItem->name, dstItem->name, srcItem->name[0]+1);
-
- itemStart += srcItem->itemSize;
- }
-
- done:
-
- HSetState ( theResource, oldResourceState );
- HSetState ( (Handle) privateData, oldInternalState );
-
- return err;
- }
-
-
-
- //
- // Build the 'cfrg' resource from our own data structures.
- //
- OSErr BuildResource ( tHeaderHan privateData, Handle theResource )
- {
- // Construct a cfrg resource from our internal template
- SignedByte oldResourceState, oldInternalState;
- OSErr err;
- unsigned long headerSize = offsetof(cfrgHeader, arrayStart);
- unsigned long bytesCopied;
- int itemCount, deletedCount = 0, index;
-
- // Construct the header by setting the destination handle to that
- // size, clearing the memory, and then inserting the version and
- // item count values
- oldResourceState = HGetState(theResource);
- oldInternalState = HGetState((Handle)privateData);
- HLock((Handle)privateData);
-
- SetHandleSize(theResource, headerSize);
- err = MemError ( );
- if ( err ) goto done;
-
- BlockClear ( *theResource, 0, headerSize );
-
- ((cfrgHeader*)(*theResource))->version = (*privateData)->version;
- itemCount = (*privateData)->itemCount;
- ((cfrgHeader*)(*theResource))->itemCount = itemCount;
-
- // Now, copy each item individually
- bytesCopied = headerSize;
- for (index = 0; index < itemCount; index++)
- {
- cfrgItem* dstPtr;
- tItemPtr srcPtr;
- long itemSize;
- unsigned long newSize;
-
- srcPtr = &(*privateData)->itemList[index];
- if ( srcPtr->bDeleted )
- {
- deletedCount++;
- continue;
- }
-
- // Calculate the size of this entry
- itemSize = offsetof(cfrgItem, name) + srcPtr->name[0] + 1;
- itemSize += itemSize & 0x0003; // Pad up to the next multiple of 4
-
- // Extend and clear the handle
- newSize = bytesCopied + itemSize;
- SetHandleSize(theResource, newSize);
- err = MemError ( );
- if ( err ) goto done;
- dstPtr = (cfrgItem*)(((unsigned long)*theResource) + bytesCopied);
- BlockClear ( (Ptr)dstPtr, 0, itemSize );
-
- // Transfer the individual fields
- dstPtr->archType = srcPtr->archType;
- dstPtr->updateLevel = srcPtr->updateLevel;
- dstPtr->currVersion = srcPtr->currVersion;
- dstPtr->oldDefVersion = srcPtr->oldDefVersion;
- dstPtr->appStackSize = srcPtr->appStackSize;
- dstPtr->appSubFolder = srcPtr->appSubFolder;
- dstPtr->usage = srcPtr->usage;
- dstPtr->location = srcPtr->location;
- dstPtr->codeOffset = srcPtr->codeOffset;
- dstPtr->codeLength = srcPtr->codeLength;
- dstPtr->itemSize = itemSize;
- BlockMove(srcPtr->name, dstPtr->name, srcPtr->name[0] + 1);
-
- bytesCopied = newSize;
- }
-
- ((cfrgHeader*)(*theResource))->itemCount -= deletedCount;
-
-
- done:
- HSetState(theResource, oldResourceState);
- HSetState((Handle)privateData, oldInternalState);
- return err;
- }
-
-
-
- OSErr CopyFragment ( tHeaderHan sourceHeader, FSSpecPtr sourceSpec, int16 sourceIndex,
- tHeaderHan targetHeader, FSSpecPtr targetSpec )
- {
- SignedByte sourceState;
- SignedByte targetState;
- int16 targetIndex = 0;
- OSErr theErr;
- tItemPtr targetPtr = nil;
-
-
- // No need to use HLockHi. It's slower, and they're not locked for long
- // enough for it to make any difference here.
- sourceState = HGetState ( (Handle) sourceHeader ); HLock ( (Handle) sourceHeader );
- targetState = HGetState ( (Handle) targetHeader ); HLock ( (Handle) targetHeader );
-
- // Resize the handle based on the new item count
- theErr = SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount + 1 );
- if ( theErr ) goto CleanupAndBail;
-
- // We're appending the data, so the target index is itemCount (Index in zero-based)
- targetIndex = (*targetHeader)->itemCount;
-
- targetPtr = &(*targetHeader)->itemList[targetIndex];
- BlockMoveData ( &(*sourceHeader)->itemList[sourceIndex], targetPtr, sizeof ( tItem ) );
- if ( sourceSpec && targetSpec )
- {
- theErr = AppendFileData ( sourceSpec, targetSpec, &targetPtr->codeOffset, &targetPtr->codeLength );
- if ( theErr ) goto CleanupAndBail;
-
- }
-
- // Success. Now it's safe to update the itemCount
- (*targetHeader)->itemCount++;
-
- return noErr;
-
-
- CleanupAndBail:
-
- // Unlock our handles
- HSetState ( (Handle) sourceHeader, sourceState );
- HSetState ( (Handle) targetHeader, targetState );
- // Release any extra storage we may have grabbed
- SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount );
-
- return theErr;
- }
-
-
-
- //
- // This is called twice to actualy delete a fragment. The first time to update
- // the data structures we keep in memory. The second time is when the user saves
- // the document, and we actually delete the fragment from the data fork.
- //
- OSErr DeleteFragment ( tHeaderHan theHeader, FSSpecPtr theSpec, int16 theIndex )
- {
- SignedByte theState;
- OSErr theErr = noErr;
- tItemPtr theItem = nil;
-
-
- theState = HGetState ( (Handle) theHeader );
- HLock ( (Handle) theHeader );
-
- theItem = GetNthItem ( theHeader, theIndex );
- if ( theItem == nil )
- return kGenericError;
-
- // If the item isn't already marked as deleted, do so
- // and update the header with the new item count.
- if ( !theItem->bDeleted )
- theItem->bDeleted = true;
-
- // If we have a file spec, we need to actually delete the data now.
- if ( theSpec )
- {
- int i;
-
- theErr = DeleteFileData ( theSpec, theItem->codeOffset, theItem->codeLength );
-
- // We need to update the offsets off all the fragments that follow this one
- for ( i = theIndex + 1; i < (*theHeader)->itemCount; i++ )
- {
- tItemPtr tmpItem;
-
- tmpItem = GetNthItem ( theHeader, i );
- tmpItem->codeOffset -= theItem->codeLength;
- }
- }
-
- HSetState ( (Handle) theHeader, theState );
-
- return noErr;
- }
-
-
-
- //
- // Gets the record pointer given an index
- //
- tItemPtr GetNthItem ( tHeaderHan theHeader, int16 theIndex )
- {
- tItemPtr theItem = nil;
-
- if ( (*theHeader)->itemCount > theIndex )
- theItem = &(*theHeader)->itemList[theIndex];
-
- #if DEBUGGING
- if ( theItem == nil ) DebugStr ( "\p GetNthItem returning nil" );
- #endif
-
- return theItem;
- }
-
-
-
- //
- // Gets the last item. Used for when an item has just been added.
- //
- tItemPtr GetLastItem ( tHeaderHan theHeader )
- {
- int16 theIndex;
- tItemPtr theItem = nil;
-
- theIndex = (*theHeader)->itemCount - 1;
- theItem = &(*theHeader)->itemList[theIndex];
-
- #if DEBUGGING
- if ( theItem == nil ) DebugStr ( "\p GetLastItem returning nil" );
- #endif
-
- return theItem;
- }
-
-
-
- //
- // Returns the number of fragments currently in the document
- //
- int16 GetItemCount ( tHeaderHan theHeader )
- {
- return (*theHeader)->itemCount;
- }
-
-
-
- //
- // Sets the handle size based on the number of items we pass in
- //
- static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount )
- {
- SignedByte theState;
- OSErr theErr;
-
- // We need to pass SetHandleSize an unlocked handle, but we'll
- // preserve its present state.
- theState = HGetState ( theHan );
- HUnlock ( theHan );
-
- // set the size of the resource based in the item count
- SetHandleSize ( theHan, offsetof ( tHeader, itemList ) +
- (itemCount * sizeof ( tItem )) );
- theErr = MemError ( );
- HSetState ( theHan, theState );
-
- return theErr;
- }
-
-
-
- //
- // Appends the data in source at given offset of length to target file.
- // The offset of the new data in target is returned in offset.
- //
- OSErr AppendFileData ( FSSpecPtr source, FSSpecPtr target, long* offset, long* length )
- {
- OSErr theErr;
- short refNum;
- Ptr theData;
- long inOutCount;
-
-
-
- //
- // TO DO:
- // Don't want to depend on having a large enough block
- // for the entire fragment. Try our heap, then temporary
- // memory, and finally resort to doing it bit by bit.
- //
-
- theErr = FSpOpenDF ( source, fsRdPerm, &refNum );
-
- // A length of zero indicates ‘until the EOF’. We'll update
- // the length now, and return it back to the caller.
- if ( *length == 0 )
- theErr = GetEOF ( refNum, length );
-
- theData = NewPtr ( *length );
- if ( theData )
- {
- inOutCount = *length;
- // On entry, ‘offset’ is the offset to the data in source file
- theErr = SetFPos ( refNum, fsFromStart, *offset );
- theErr = FSRead ( refNum, &inOutCount, theData );
- FSClose ( refNum );
-
- FSpOpenDF ( target, fsRdWrPerm, &refNum );
- theErr = SetFPos ( refNum, fsFromLEOF, 0L );
- // On exit, ‘offset’ is the offset to the data in target file
- theErr = GetFPos ( refNum, offset );
- inOutCount = *length;
- theErr = FSWrite ( refNum, &inOutCount, theData );
-
- // Dispose of the buffer
- DisposePtr ( theData );
- }
-
- // Close the file - ‘source’ if memory alloc failed, ‘target’ if sucessful
- FSClose ( refNum );
-
-
- return noErr;
- }
-
-
-
- //
- // Deletes the block of data from the data fork at a given offset and length.
- // It achieves this in one of two ways. If the data to be deleted is at the
- // logical EOF, it simply resets the logical EOF to the given offset. If data
- // exists after the data to be deleted, that data is read and writen back at
- // the given offset.
- //
- static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength )
- {
- OSErr theErr;
- int16 theRef;
- int32 theEOF;
- int32 inOutCount;
- Ptr theData;
-
-
-
- theErr = FSpOpenDF ( theSpec, fsRdWrPerm, &theRef );
- if ( theErr )
- return theErr;
-
- theErr = GetEOF ( theRef, &theEOF );
-
- // A length of zero indicates ‘until the EOF’
- if ( theLength == 0 )
- theLength = theEOF - theOffset;
-
-
- // Is the block to be removed at the EOF? For readablility, I'll keep
- // this ‘if’ seperate from the above (length == 0) special condition.
- if ( theEOF == theOffset + theLength )
- theErr = SetEOF ( theRef, theOffset );
- else
- {
- // Data exists after the block to be deleted
- theData = NewPtr ( theEOF - (theOffset + theLength) );
- if ( theData )
- {
- theErr = SetFPos ( theRef, fsFromStart, theOffset + theLength );
- inOutCount = theEOF - (theOffset + theLength);
- theErr = FSRead ( theRef, &inOutCount, theData );
-
- theErr = SetFPos ( theRef, fsFromStart, theOffset );
- inOutCount = theEOF - (theOffset + theLength);
- theErr = FSWrite ( theRef, &inOutCount, theData );
-
- theErr = SetEOF ( theRef, theOffset + inOutCount );
- }
- }
-
- FSClose ( theRef );
-
- return noErr;
- }
-
-
-
- //
- // Each fragment's data stored in a temp file has a single temp
- // record. It includes a usage count and the fileSpec. If an item
- // doesn't have a record, this routine creates it as it increments
- // its usage count.
- //
- OSErr IncrementTempUsageCount ( tItemPtr theItem )
- {
- OSErr theErr;
-
- if ( theItem->tempFilePtr == nil )
- {
- theItem->tempFilePtr = (tTempFilePtr) NewPtrClear ( sizeof ( tTempFileRec ) );
- theErr = MemError ( );
- if ( theErr )
- return theErr;
-
- }
-
- theItem->tempFilePtr->usageCount++;
-
- return noErr;
- }
-
-
-
- //
- // As each document gets its own copy of the fragment, it decrements the
- // usage count. Finally, the temp file is deleted and this storage freed.
- //
- OSErr DecrementTempUsageCount ( tItemPtr theItem )
- {
- OSErr theErr;
-
-
- if ( theItem->tempFilePtr )
- {
- theItem->tempFilePtr->usageCount--;
- if ( theItem->tempFilePtr->usageCount == 0 )
- {
- theErr = FSpDelete ( &theItem->tempFilePtr->fileSpec );
- if ( theErr )
- return theErr;
-
- DisposePtr ( (Ptr) theItem->tempFilePtr );
- theErr = MemError ( );
- if ( theErr )
- return theErr;
-
- }
- theItem->tempFilePtr = nil;
- }
-
-
- return noErr;
- }
-
-
-
- //
- // Find out how many documents are using this temp record
- //
- int GetTempUsageCount ( tItemPtr theItem )
- {
- int theCount = 0;
-
- if ( theItem->tempFilePtr )
- theCount = theItem->tempFilePtr->usageCount;
-
- return theCount;
- }
-
-
-
- //
- // Get the fileSpec for the fragments data
- //
- FSSpecPtr GetTempSpecPtr ( tItemPtr theItem )
- {
- FSSpecPtr theSpec = nil;
-
- if ( theItem->tempFilePtr )
- theSpec = &theItem->tempFilePtr->fileSpec;
-
-
- #if DEBUGGING
- if ( theSpec == nil ) DebugStr ( "\p GetTempSpecPtr returning nil" );
- #endif
-
- return theSpec;
- }
-
-
-
-
-
-